﻿using System;
using System.Collections.ObjectModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace System.Windows.Controls
{
    /// <summary>
    /// Defines an interface for an object which manages the arrangement in the <see cref="CloudView"/> control.
    /// </summary>
    public interface ICloudArranger
    {
        /// <summary>
        /// Should return the display name or title for the arrangement mode itself
        /// </summary>
        string Title { get; }

        /// <summary>
        /// Should return the horizontal axis label for the minimum, or left most label
        /// </summary>
        string MinAxisLabel { get; }

        /// <summary>
        /// Should return the horizontal axis label for the maximum, or right most label
        /// </summary>
        string MaxAxisLabel { get; }

        /// <summary>
        /// Should return the name of the field or property which is used to determine the vertical axis sort of elements
        /// </summary>
        string VerticalAxisField { get; set; }

        /// <summary>
        /// Should return the name of the field or property which is used to determine the horizonal axis sort of elements
        /// </summary>
        string HorizontalAxisField { get; set; }

        /// <summary>
        /// Should return the name of the field or property which is used to determine the vertical axis sort of elements
        /// </summary>
        bool VerticalUseAbsoluteMinimum { get; set; }

        /// <summary>
        /// Method for Arranging the children of a <see cref="CloudPanel"/>
        /// </summary>
        /// <param name="items">The collection of <see cref="CloudViewItems"/> to be arranged</param>
        /// <param name="viewportSize">A <see cref="System.Windows.Size"/> defining the size of the viewport surface</param>
        void ArrangeChildren(CloudViewItems items, Size viewportSize);
    }

    /// <summary>
    /// Implements a collection of <see cref="ICloudArranger"/> instances.
    /// </summary>
    public class CloudArrangers : ObservableCollection<ICloudArranger>
    {
        /// <summary>
        /// Instantiates a <see cref="CloudArrangers"/> class.
        /// </summary>
        public CloudArrangers()
        {

        }

        /// <summary>
        /// Returns a static (Singleton) instance of <see cref="CloudArrangers"/>.
        /// </summary>
        public static CloudArrangers Default
        {
            get
            {
                CloudArrangers items = new CloudArrangers();
                items.Add(new AlphabeticalCloudArranger());
                items.Add(new DateCloudArranger());
                return items;
            }
        }
    }

    /// <summary>
    /// Implements an abstract implementation of the <see cref="ICloudArranger"/> interface.
    /// </summary>
    public abstract class CloudArranger : ICloudArranger
    {
        /// <summary>
        /// An abstract property get for returning the Title for the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public abstract string Title { get; }

        /// <summary>
        /// An abstract property get for returning the MinAxisLabel for the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public abstract string MinAxisLabel { get; }

        /// <summary>
        /// An abstract property get for returning the MaxAxisLabel for the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public abstract string MaxAxisLabel { get; }

        /// <summary>
        /// Gets/Sets the name of the field or property used to arrange items vertically by the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public string   VerticalAxisField { get; set; }

        /// <summary>
        /// Gets/Sets the name of the field or property used to arrange items horizontally by the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public string   HorizontalAxisField { get; set; }

        /// <summary>
        /// Gets/Sets the boolean value which indicates whether zero or the actual minimum value is to be used when calculating the vertical range displacement by the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        public bool     VerticalUseAbsoluteMinimum { get; set; }

        private CloudSorter hSort;
        private CloudSorter vSort;

        /// <summary>
        /// Returns a <see cref="CloudSorter"/> instance which handles the horizontal sorting for the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        protected virtual CloudSorter HorizontalSorter
        {
            get
            {
                if (hSort == null)
                {
                    hSort = new CloudSorter(this.HorizontalAxisField, SortDirections.Ascending);
                }
                else
                {
                    hSort.SortField = this.HorizontalAxisField;
                    hSort.SortDirection = SortDirections.Ascending;
                }

                return hSort;
            }
        }

        /// <summary>
        /// Returns a <see cref="CloudSorter"/> instance which handles the vertical sorting for the <see cref="ICloudArranger"/> implementation.
        /// </summary>
        protected virtual CloudSorter VerticalSorter
        {
            get
            {
                if (vSort == null)
                {
                    vSort = new CloudSorter(this.VerticalAxisField, SortDirections.Ascending);
                }
                else
                {
                    vSort.SortField = this.VerticalAxisField;
                    vSort.SortDirection = SortDirections.Descending;
                }

                return vSort;
            }
        }

        /// <summary>
        /// Provides a default and virtual implementation for the arrangement of <see cref="CloudViewItems"/>.
        /// </summary>
        public virtual void ArrangeChildren(CloudViewItems items, Size viewportSize)
        {
            if (items.Count > 0)
            {
                //Sort for the X Axis
                items.Sort(this.HorizontalSorter);

                //Assign X values based on count (linear spread for alphabetical)
                object maxVal = CloudSorter.GetSortValue(this.HorizontalAxisField, items[0].DataContext);
                object minVal = CloudSorter.GetSortValue(this.HorizontalAxisField, items[items.Count - 1].DataContext);

                for (int index = 0; index < items.Count; index++)
                {
                    object valX = CloudSorter.GetSortValue(this.HorizontalAxisField, items[index].DataContext);

                    if (this.HorizontalSorter.SortDirection == SortDirections.Ascending)
                        items[index].X = CloudSorter.GetProportionalValue(valX, minVal, maxVal) * viewportSize.Width;
                    else
                        items[index].X = (1.0 - CloudSorter.GetProportionalValue(valX, minVal, maxVal)) * viewportSize.Width;
                }

                //Sort for the Y Axis
                items.Sort(this.VerticalSorter);

                //Get the Max and Min Values (the 1st and Last elements)
                maxVal = CloudSorter.GetSortValue(this.VerticalAxisField, items[0].DataContext);
                minVal = CloudSorter.GetSortValue(this.VerticalAxisField, items[items.Count - 1].DataContext);
                if (this.VerticalUseAbsoluteMinimum && (IsNumeric(minVal))) minVal = GetZeroVal(minVal);

                //Assign Y values based on range of values
                for (int index = 0; index < items.Count; index++)
                {
                    object valY = CloudSorter.GetSortValue(this.VerticalAxisField, items[index].DataContext);

                    if (this.VerticalSorter.SortDirection == SortDirections.Ascending)
                        items[index].Y = CloudSorter.GetProportionalValue(valY, minVal, maxVal) * viewportSize.Height;
                    else
                        items[index].Y = (1.0 - CloudSorter.GetProportionalValue(valY, minVal, maxVal)) * viewportSize.Height;
                }
            }
        }

        /// <summary>
        /// A method which returns the equivalent zero value for a given type
        /// </summary>
        /// <param name="val">The current value</param>
        /// <returns>A value of the same type which is the equivalent to a logical zero</returns>
        protected object GetZeroVal(object val)
        {
            if (val == null) return 0;
            return Convert.ChangeType(0, val.GetType(), System.Globalization.CultureInfo.CurrentUICulture.NumberFormat);
        }

        /// <summary>
        /// A helper method which determines if a data bound value is a numeric type
        /// </summary>
        /// <param name="val">The value to examine</param>
        /// <returns>A boolean value indicating the nature of the value</returns>
        protected bool IsNumeric(object val)
        {
            if (val == null) return false;
            if (val is double || val is int || val is long || val is uint || val is ulong) return true;
            return false;
        }
    }

    /// <summary>
    /// Implements a <see cref="CloudArranger"/> which arranges items vertically by Relevency and horizontally by Title.
    /// </summary>
    public class AlphabeticalCloudArranger : CloudArranger
    {
        /// <summary>
        /// Instantiates an <see cref="AlphabeticalCloudArranger"/> instance.
        /// </summary>
        public AlphabeticalCloudArranger()
        {
            this.VerticalAxisField = "Relevancy";
            this.HorizontalAxisField = "Title";
            this.VerticalUseAbsoluteMinimum = true;
        }

        #region ICloudArranger Members

        /// <summary>
        /// Returns the title for the Arranger
        /// </summary>
        public override string Title
        {
            get { return SystemResources.ALPHA_CLOUD_ARRANGER; }
        }

        /// <summary>
        /// Returns the Mininum Axis Label for the Arranger
        /// </summary>
        public override string MinAxisLabel
        {
            get { return SystemResources.ALPHA_CLOUD_MIN; }
        }

        /// <summary>
        /// Returns the Maximum Axis Label for the Arranger
        /// </summary>
        public override string MaxAxisLabel
        {
            get { return SystemResources.ALPHA_CLOUD_MAX; }
        }

        /// <summary>
        /// Arranges the <see cref="CloudViewItems"/> based on the properties of the <see cref="AlphabeticalCloudArranger"/>.
        /// </summary>
        public override void ArrangeChildren(CloudViewItems items, Size viewportSize)
        {
            if (items.Count > 0)
            {
                //Sort for the X Axis
                items.Sort(this.HorizontalSorter);

                //Assign X values based on count (linear spread for alphabetical)
                double intervalX = viewportSize.Width / (items.Count + 1);
                double originX = intervalX;

                for (int index = 0; index < items.Count; index++)
                {
                    items[index].X = originX;
                    originX += intervalX;
                }

                //Sort for the Y Axis
                items.Sort(this.VerticalSorter);

                //Get the Max and Min Values (the 1st and Last elements)
                object maxVal = CloudSorter.GetSortValue(this.VerticalAxisField, items[0].DataContext);
                object minVal = CloudSorter.GetSortValue(this.VerticalAxisField, items[items.Count - 1].DataContext);
                if (this.VerticalUseAbsoluteMinimum && (IsNumeric(minVal))) minVal = GetZeroVal(minVal);

                //Assign Y values based on range of values
                for (int index = 0; index < items.Count; index++)
                {
                    object valY = CloudSorter.GetSortValue(this.VerticalAxisField, items[index].DataContext);

                    if (this.VerticalSorter.SortDirection == SortDirections.Ascending)
                        items[index].Y = CloudSorter.GetProportionalValue(valY, minVal, maxVal) * viewportSize.Height;
                    else
                        items[index].Y = (1.0 - CloudSorter.GetProportionalValue(valY, minVal, maxVal)) * viewportSize.Height;
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// Implements a <see cref="CloudArranger"/> which arranges items vertically by Relevency and horizontally by Date.
    /// </summary>
    public class DateCloudArranger : CloudArranger
    {
        /// <summary>
        /// Instantiates a <see cref="DateCloudArranger"/> instance.
        /// </summary>
        public DateCloudArranger()
        {
            this.VerticalAxisField = "Relevancy";
            this.HorizontalAxisField = "Date";
            this.VerticalUseAbsoluteMinimum = true;
        }

        #region ICloudArranger Members

        /// <summary>
        /// Returns the title for the Arranger
        /// </summary>
        public override string Title
        {
            get { return SystemResources.CREATED_CLOUD_ARRANGER; }
        }

        /// <summary>
        /// Returns the Minimum Axis Label for the Arranger
        /// </summary>
        public override string MinAxisLabel
        {
            get { return SystemResources.CREATED_CLOUD_MIN; }
        }

        /// <summary>
        /// Returns the Maximum Axis Label for the Arranger
        /// </summary>
        public override string MaxAxisLabel
        {
            get { return SystemResources.CREATED_CLOUD_MAX; }
        }

        /// <summary>
        /// (Override) Returns a Horizontal Sorter (<see cref="CloudSorter"/>) which always sorts in a descending fashion
        /// </summary>
        protected override CloudSorter HorizontalSorter
        {
            get
            {
                CloudSorter sorter = base.HorizontalSorter;
                sorter.SortDirection = SortDirections.Descending;
                return sorter;
            }
        }

        #endregion
    }
}
